# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##--------------------------------------------------------------------------##
"""
Stack <- BindStack
  Implement basic canvas binding methods for the stack. Canvas events are
  bound to a set of methods for subclass. Depends on various methods of Stack.
"""
__all__ = ['BindStack_Methods']

import abc
import logging

LOGGER = logging.getLogger('PySolFC.PileLib.Bind')

from ..viewlib.constants import (CURSOR_DOWN_ARROW, EVENT_HANDLED,
                                 EVENT_PROPAGATE,)
from ..settings import CONF_OPT
from ..gamelib import state


class BindStack_Methods(object):
    """
    Implement basic canvas bindings for the stack. Canvas events are bound to
    a set of methods for subclass.
    """

    def initBindings(self):
        """
        initBindings() -> None

        Initialize canvas bindings.
        """
        group = self.group
        group.bind('<1>', self.__clickEventHandler)
        group.bind('<Motion>', self.__motionEventHandler)
        group.bind('<ButtonRelease-1>', self.__releaseEventHandler)
        group.bind('<Control-1>', self.__controlclickEventHandler)
        group.bind('<Shift-1>', self.__shiftclickEventHandler)
        group.bind('<Double-1>', self.__doubleclickEventHandler)
        group.bind('<3>', self.__rightclickEventHandler)
        group.bind('<2>', self.__middleclickEventHandler)
        group.bind('<Control-3>', self.__middleclickEventHandler)
        group.bind('<Enter>', self.__enterEventHandler)
        group.bind('<Leave>', self.__leaveEventHandler)

    # Public handlers for Canvas events called by the private handlers
    def clickHandler(self, event):
        """
        clickHandler(event:obj) -> bool

        Stub for handling <left-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    def middleclickHandler(self, event):
        """
        middleclickHandler(event:obj) -> bool

        Stub for handling <middle-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    def rightclickHandler(self, event):
        """
        rightclickHandler(event:obj) -> bool

        Stub for handling <right-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    def doubleclickHandler(self, event):
        """
        doubleclickHandler(event:obj) -> bool

        Handle <dbl-left-click> as <left-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return self.clickHandler(event)

    def controlclickHandler(self, event):
        """
        controlclickHandler(event:obj) -> bool

        Stub for handling a <ctl-left-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    def controlmiddleclickHandler(self, event):
        """
        controlmiddleclickHandler(event:obj) -> bool

       Stub for handling a <ctl-middle-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    def shiftclickHandler(self, event):
        """
        shiftclickHandler(event:obj) -> bool

        Stub for handling a <shift-left-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    def shiftrightclickHandler(self, event):
        """
        shiftrightclickHandler(event:obj) -> bool

        Stub for handling a <shift-right-click> on the stack.

        :param object event: canvas event.
        :return: event state
        :rtype: True | False | None
        """
        return False

    # Private handlers for Canvas events which call the public handlers.
    def __defaultClickEventHandler(self, event, handler, start_drag=False,
                                   cancel_drag=True):
        """
        Public handlers should return True | False | None. Only False and None
        will initiate a drag event when start_drag is True. False will also
        enable sound fx for event as well.

        :param object event: canvas event.
        :param function handler: function handler.
        :keyword boolean start_drag: handler may start a drag operation.
        :keyword boolean cancel_drag: handler may cancel a drag operation.
        :return: event status
        """
        self.game.event_handled = True
        if self.game.is_demo:
            self.game.demo_stop()  # Stop demo_mode
            return EVENT_HANDLED
        if self.game.busy:
            return EVENT_HANDLED
        if self.game.drag['stack'] and cancel_drag:
            self.game.drag['stack'].cancelDrag(event)
        if start_drag:
            r = handler(event)
            if not r:
                self.startDrag(event, sound=r is False)
        else:
            handler(event)
        return EVENT_HANDLED

    def __clickEventHandler(self, event):
        """
        Handle differences between supported mouse_type.

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        if CONF_OPT['mouse_type'] == 'drag-n-drop':
            cancel_drag = start_drag = True
            handler = self.clickHandler
        else:
            cancel_drag, start_drag = False, not self.game.drag['stack']
            handler = self.clickHandler if start_drag else self.finishDrag
        return self.__defaultClickEventHandler(event, handler, start_drag,
                                               cancel_drag)

    def __doubleclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with doubleclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event, self.doubleclickHandler)

    def __middleclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with middleclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event, self.middleclickHandler)

    def __controlmiddleclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with controlmiddleclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event,
                                               self.controlmiddleclickHandler)

    def __rightclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with rightclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event, self.rightclickHandler)

    def __controlclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with controlclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event,
                                               self.controlclickHandler)

    def __shiftclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with shiftclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event, self.shiftclickHandler)

    def __shiftrightclickEventHandler(self, event):
        """
        Call __defaultClickEventHandler() with shiftrightclickHandler().

        :param object event: canvas event.
        :return: __defaultClickEventHandler
        """
        return self.__defaultClickEventHandler(event,
                                               self.shiftrightclickHandler)

    def __motionEventHandler(self, event):
        """
        :param object event: canvas event.
        :return: event status
        """
        if not (self.game.drag['stack'] or self is self.game.drag['stack']):
            return EVENT_PROPAGATE
        self.game.demo_stop()  # Stop demo_mode
        if self.game.busy or CONF_OPT['mouse_type'] == 'point-n-click':
            return EVENT_HANDLED
        self.keepDrag(event)
        return EVENT_HANDLED

    def __releaseEventHandler(self, event):
        """
        :param object event: canvas event.
        :return: event status
        """
        self.game.demo_stop()   # Stop demo_mode
        if self.game.busy:
            return EVENT_HANDLED
        if CONF_OPT['mouse_type'] == 'drag-n-drop':
            self.keepDrag(event)
            self.finishDrag(event)
        return EVENT_HANDLED

    @state.not_state('is_preview')
    def __show_assistance(self, show=False):
        """
        Update ncards on status bar or help bar text.
        """
        from ..viewlib.console import update_console
        kws = {'assist': '', 'info': ''}
        if show:
            kws = {'assist': self._helpString, 'info': self.getNumCards()}
        update_console(**kws)

    def __enterEventHandler(self, event):
        """
        :param object event: canvas event.
        :return: event status
        """
        if self.game.drag['stack']:
            if CONF_OPT['mouse_type'] == 'point-n-click':
                if self.cardsBuild(self.game.drag['stack'],
                                     self.game.drag['cards']):
                    self.canvas.config(cursor=CURSOR_DOWN_ARROW)
                    self.current_cursor = CURSOR_DOWN_ARROW
                    self.cursor_changed = True
        else:
            self.canvas.after_idle(self.__show_assistance, True)
        return EVENT_HANDLED

    def __leaveEventHandler(self, event):
        """
        :param object event: canvas event.
        :return: event status
        """
        if not self.game.drag['stack']:
            self.canvas.after_idle(self.__show_assistance, False)
        if CONF_OPT['mouse_type'] == 'drag-n-drop':
            return EVENT_HANDLED
        if self.cursor_changed:
            self.canvas.config(cursor='')
            self.current_cursor = ''
            self.cursor_changed = False
        drag_stack = self.game.drag['stack']
        if self is drag_stack:
            x, y = event.x, event.y
            w, h = self.canvas.winfo_width(), self.canvas.winfo_height()
            #{ cancel drag
            if x < 0 or y < 0 or x >= w or y >= h:
                drag_stack.cancelDrag(event)
                self.canvas.after_idle(self.__show_assistance, False)
                return EVENT_HANDLED
            #{ continue drag
            else:
                return self.__motionEventHandler(event)
        else:
            return EVENT_PROPAGATE